/*
 *  sample C  -- symbol table definition and manipulation
 */
#ifndef _SYMBOL_C
#define _SYMBOL_C

#include <stdlib.h> 
#include <string.h> 
#include "symbol.h"
#include "sintax.h"
#include "interprete.h"
/*
 *  Symbol table
 */

WORDPTR global = NULL,             /* global end of chain */
        local = NULL;              /* local end of chain */

/*
 * Block Table
 */

int blockLevel = 0 ;  /* current static block depth */

/*
 * show an entry 
 */

void show(WORDPTR ptr)
{
  if (ptr != NULL) {
    if (! ptr->name) bug( "show null name");
    printf("%d - ", blockLevel);
    printf("[%s - %d - ", ptr->name, ptr->blockLevel);
    switch (ptr->kind) {
      case UDEC  : printf(" UDEC ");  break; /* not declared */
      case FUNC  : printf(" FUNC ");  break; /* fuction */
      case UFUNC : printf(" UFUNC "); break; /* undefined function */
      case VAR   : printf(" VAR ");   break; /* declared variable */
      case TYPE  : printf(" TYPE ");  break; /* declared type */
      case PARAM : printf(" PARAM "); break; /* undeclared parameter */
    }
    if (ptr->type != NULL) {
      printf("- %s ", ptr->type->name);
    }
    if(ptr->kind == PARAM || ptr->kind == VAR)
       printf("- %d",ptr->offset);
    printf("] \n");
  } /* if */ 
} /* show */

void showTable(WORDPTR ptr){
   ptr=(ptr)?ptr:local;
   printf("tabla simbolos\n");
   while(ptr){
      show(ptr);
      if(ptr->kind == FUNC){
         WORDPTR temp = ptr->S.link;
         while(temp){
            show(temp);
            temp = temp->S.link;
         }
      }
      ptr = ptr->next;
   }
   printf("fin tabla\n");
}

//char* strsave();
//void copyParams(WORDPTR funct, WORDPTR list){
//   while(list){
//      WORDPTR nuevo = (WORDPTR)malloc(sizeof(WORD));
//      nuevo->name = strsave(list->name);
//      nuevo->kind = PARAM;
//      nuevo->blockLevel = list->blockLevel;
//      nuevo->type = list->type;
//      nuevo->offset = list->offset;
//      funct->S.link = nuevo;
//      list=list->S.link;
//      funct = nuevo;
//   }
//}


/*
 *  strsave
 */
char * strsave( char *s )
{
  char * cp = (char *) malloc( strlen(s)+1);

  if (cp)
  {
    strcpy(cp, s);
    return cp;
  }
  error("No more room to save strings");
  return NULL;
}/* strsave */

/*
 *   Add a new name to local region
 */

/*
 *  local points to a blind element, wich never changes, and after which
 *  new entries are added. Along the chain, at the bottom of the stack,
 *  are essencially never popped.
 *  The function create() will add a new, local entry to the symbol
 *  table for the name given as an argument.
 *  The new entry is marked as being undeclared.
 */

WORDPTR create( char * name )
{
  WORDPTR newEntry;

  newEntry = (WORDPTR) malloc( sizeof(WORD));

  if ( newEntry )
  {
    newEntry->next = local;
    local = newEntry;
    newEntry->name = strsave(name);
    newEntry->kind = UDEC;
    newEntry->blockLevel = blockLevel;
    newEntry->type = NULL;
    newEntry->S.num = NOTSET;
    newEntry->S.link = NULL;

    return newEntry;
  }
  fatal( "No more room for symbols." );
} /* create */


/*
 *  move an entry from local to global region
 */


/*
 * if a function description is moved to the global end of the chain,
 * it is moved to follow "global" and "global" is then pointed to it.
 * This is acomplised by this routine, which has an argument a pointer
 * to the entry to be moved
 */

void move( WORDPTR symbol)
{

  if (symbol != NULL && local != NULL && global != NULL){
  /* unlink it from its present position */
  local = local->next;

  /* relink at global end of symtab */
  global->next = symbol;
  symbol->next = NULL;
  global = symbol;
  }
} /* move */


/*
 *  locate entry by name
 */

WORDPTR find( char * name)
{
  WORDPTR ptr;

  /* search symtab until match or end of symtab chain */
  ptr = local; 
  while (ptr)
  { 
    if ( ! ptr->name ) bug("Name not find");
    else
      /* return ptr if names match */
      if ( strcmp(ptr->name,name) == 0 ) return ptr;
    ptr = ptr->next;
  }

  /* search fails, return NULL */
  return (WORDPTR) 0;

}/* find */


/*
 *  interface for lexical analyzer:
 *  locate or enter Identifier, save text of Constant
 */

WORDPTR lookUp( char *s )
{
  WORDPTR p;

  p = find(s);

  if ( p ) return p;
  return create(s);
} /* lookUp */


/* 
 *  mark entry as part of parameterList
 */

WORDPTR linkParam( WORDPTR symbol, WORDPTR next)
{
  switch( symbol->kind )
  {
    case PARAM : error("duplicate parameter %s", symbol->name);
                 return next;
    case FUNC  :
    case UFUNC :
    case VAR   : symbol = create( symbol->name);   
    case UDEC  : break;
    default    : bug("linkParam");
  } /* switch ...*/

  symbol->kind = PARAM;
  symbol->blockLevel = blockLevel;
  symbol->S.link = next;
  return symbol;

} /* linkParam */



/*
 *  declare parameter
 */

WORDPTR makeParam( WORDPTR symbol)
{
  switch( symbol->kind )
  {
    case VAR   : if (symbol->blockLevel == 2) {
                   error("parameter %s declare twice", symbol->name);
                   return symbol;
                 } 
    case UDEC  : 
    case FUNC  :
    case UFUNC : error("%s is not a parameter", symbol->name);
                 symbol = create(symbol->name);
    case PARAM : break;
    default    : bug("makeParam");
  } /* switch ...*/

  symbol->kind = VAR;
  symbol->blockLevel = blockLevel;
  return symbol;

} /* makeParam */

/*
 *  define a variable
 */

WORDPTR makeVar( WORDPTR symbol)
{
  switch( symbol->kind )
  {
    case VAR   :
    case FUNC  :
    case UFUNC : if ((symbol->blockLevel == blockLevel) || ((symbol->blockLevel == 2) && (blockLevel == 3)))
                   error("duplicate name %s", symbol->name);
                 symbol = create(symbol->name);
    case UDEC  : break;
    case PARAM : error(" unexpected parameter %s", symbol->name);
                 break;
    default    : bug("makeVar");
  } /* switch ...*/

  symbol->kind = VAR;
  symbol->blockLevel = blockLevel;
  return symbol;

} /* makeVar */


/*
 * define a function
 */

WORDPTR makeFunc( WORDPTR symbol)
{
  switch( symbol->kind )
  {
    case UFUNC :
    case UDEC  : break;
    case VAR   : error("function name %s same as global variable", symbol->name);
                 return symbol;
    case FUNC  : error("duplicate function definition %s", symbol->name);
                 return symbol;
    default    : bug("makeFunc");
  } /* switch ...*/

  symbol->kind = FUNC;
  symbol->blockLevel = 1;
  
  return symbol;

} /* makeFunc */


/*
 * set of verify number of parameters
 */

void checkParam( WORDPTR symbol, int count)
{
  if ( symbol->S.num == NOTSET) symbol->S.num = count;
  else if ( symbol->S.num != count)
    warning( "function %s should have %d argument(s)", symbol->name, symbol->S.num);

} /* checkParam */


/*
 * default undeclared parameters, count
 */
int paramDefault( WORDPTR symbol)
{
  int count = 0;
  while (symbol)
  {
    ++count;
    if ( symbol->kind == PARAM ){ 
       symbol->kind = VAR; 
       symbol->type = find("long");
    }
    symbol = symbol->S.link;
  }
  return count;

} /* ParamDefault*/


/*
 * check reference or assignment to variable
 */

void checkVar( WORDPTR symbol)
{
  switch( symbol->kind )
  {
    case UDEC  : error( "undeclared variable %s", symbol->name);
                 break;
    case PARAM : error( "unexpected parameter %s", symbol->name);
                 break;
    case UFUNC :
    case FUNC  : error( "function %s used as variable", symbol->name);
    case VAR   : return ;
    default    : bug("checkVar");
  } /* switch ...*/

  symbol->kind = VAR;
  symbol->blockLevel = blockLevel;
  
} /* checkVar */


/*
 * check reference to function, implicitly declare it 
 */

void checkFunc( WORDPTR symbol)
{
  switch( symbol->kind )
  {
    case UDEC  : break;
    case PARAM : error( "unexpected parameter %s", symbol->name);
                 symbol->S.num = NOTSET;
                 return;
    case VAR   : error( "variable %s used as function", symbol->name);
                 symbol->S.num = NOTSET;
    case UFUNC :
    case FUNC  : return;    
    default    : bug("checkFunc");
  } /* switch ...*/

  symbol->kind = UFUNC;
  symbol->S.link = NULL;
  symbol->blockLevel = 1;
  move( symbol);

  
} /* checkFunc */

/*
 * Define a type 
 */

void addType( WORDPTR symbol, WORDPTR type)
{
  symbol->type = type;
} /* addType */

/*
 * Compatible types
 */

WORDPTR checkType( WORDPTR type1, WORDPTR type2)
{
  if (type1 == NULL || type2 == NULL)  error ("undefined type in symbols");
  if (type1 == type2) 
     return type1;
  else
     if(type1->offset > type2->offset){
        emit(CALL,PB,type1->offset,_TO22);
        return type1;
     }     
     else{
        emit(CALL,PB,type2->offset,_TO21);
        return type2;
     }
} /*checkType*/

/*
 * Return types
 */

WORDPTR getType( WORDPTR symbol)
{
  if (symbol == NULL)  bug ("undefined symbol");
  if (symbol->type == NULL)  error ("undefined type in symbol %s", symbol->name);
  return symbol->type;
} /*getType*/


/*
 *    push block table
 */

void blockPush()
{
  ++blockLevel;

} /* blockPush */


/*
 *    pop block table
 */

void blockPop()
{
  WORDPTR ptr;
  
  ptr = local;
  if (ptr != NULL) {
    while ((ptr->blockLevel >= blockLevel) || (ptr->blockLevel == 0) )
    {
      if (! ptr->name) bug( "blockPop null name");

      if (ptr->kind == UFUNC) error("undefined function %s", ptr->name);
      free( ptr->name);
    
      local = ptr->next;
      free( ptr);

      ptr = local;
      if (ptr == NULL) break;
    } /* while ... */
  } 

  --blockLevel;

} /* blockPop */


/*
 * initialize symbol and block table
 */

void init()
{
  WORDPTR ptr;
  blockPush();

  global = create( "int");
  global->kind = TYPE;
  global->offset = 1;
  ptr = create( "long");
  ptr->kind = TYPE;
  ptr->offset = 2;
  ptr = create( "enum");
  ptr->kind = TYPE;
  ptr->offset = 1;
  ptr = create( "main");
  ptr->kind = UFUNC;

} /* init */


#endif

